/*
 / _____)             _              | |
( (____  _____ ____ _| |_ _____  ____| |__
 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 _____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
    (C)2016 Semtech

Description: Handling of the node configuration protocol

License: Revised BSD License, see LICENSE.TXT file include in the project

Maintainer: Miguel Luis, Gregory Cristian and Matthieu Verdy
*/
#include "sx126x-hal.h"

#include "py/runtime.h"
#include "py/objlist.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "py/mperrno.h"
#include "py/mphal.h"
#include "pin.h"
#include "spi.h"

typedef struct _sx126x_obj_t {
    // mp_obj_base_t base;
    // mp_uint_t cris_state;
    const spi_t *spi;
    const pin_obj_t *nss;
    const pin_obj_t *rst;
	const pin_obj_t	*busy;
	const pin_obj_t *irq;
    uint8_t socket_used;
} sx126x_obj_t;

STATIC sx126x_obj_t		sx126x_obj;

#define BOARD_TCXO_WAKEUP_TIME  5 // no TCXO

/*!
 * \brief Used to block execution waiting for low state on radio busy pin.
 */
#define WaitOnBusy( )             while( mp_hal_pin_read(sx126x_obj.busy) == 1 );

#define NSS_0					mp_hal_pin_low(sx126x_obj.nss)
#define NSS_1					mp_hal_pin_high(sx126x_obj.nss)
/*!
 * \brief Used to block execution to give enough time to Busy to go up
 *        in order to respect Tsw, see datasheet ยง8.3.1
 */
#define WaitOnCounter( )          for( uint8_t counter = 0; counter < 15; counter++ ) \
                                  {  __NOP( ); }

                                  
// This code handles cases where assert_param is undefined
#ifndef assert_param
#define assert_param( ... )
#endif


void Reset( void )
{
	mp_hal_pin_low(sx126x_obj.rst);
    mp_hal_delay_ms(20); // Wait 20ms
    mp_hal_pin_high(sx126x_obj.rst);
    mp_hal_delay_ms(10); // Wait 15ms
}

void Wakeup( void )
{
	uint8_t txData;

    //NSS = 0;
    NSS_0;

	txData = (uint8_t)RADIO_GET_STATUS;
	HAL_SPI_Transmit(sx126x_obj.spi->spi, &txData, 1, HAL_MAX_DELAY);

	txData = (uint8_t)0;
	HAL_SPI_Transmit(sx126x_obj.spi->spi, &txData, 1, HAL_MAX_DELAY);

    //NSS = 1;
    NSS_1;

    // Wait for chip to be ready.
    WaitOnBusy( );
}

void WriteCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size )
{
	uint8_t txData;
  
    WaitOnBusy( );

    //NSS = 0;
    NSS_0;

	txData = (uint8_t)command;
	HAL_SPI_Transmit(sx126x_obj.spi->spi, &txData, 1, HAL_MAX_DELAY);

	HAL_SPI_Transmit(sx126x_obj.spi->spi, buffer, size, HAL_MAX_DELAY);

    //NSS = 1;
    NSS_1;

    WaitOnCounter( );
}

void ReadCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size )
{
    uint8_t status = 0;
    uint8_t txData;

    WaitOnBusy( );

    //NSS = 0;
    NSS_0;

	txData = (uint8_t)command;
	HAL_SPI_Transmit(sx126x_obj.spi->spi, &txData, 1, HAL_MAX_DELAY);

	HAL_SPI_Transmit(sx126x_obj.spi->spi, &status, 1, HAL_MAX_DELAY);

    HAL_SPI_Receive(sx126x_obj.spi->spi, buffer, size, HAL_MAX_DELAY);

    //NSS = 1;
    NSS_1;
}

void WriteRegister( uint16_t address, uint8_t *buffer, uint16_t size )
{
	uint8_t	txData[3];

    WaitOnBusy( );

    //NSS = 0;
    NSS_0;

	txData[0] = (uint8_t)RADIO_WRITE_REGISTER;
	txData[1] = (uint8_t)(( address & 0xFF00 ) >> 8);
	txData[2] = (uint8_t)(address & 0x00FF );
	HAL_SPI_Transmit(sx126x_obj.spi->spi, txData, 3, HAL_MAX_DELAY);

	HAL_SPI_Transmit(sx126x_obj.spi->spi, buffer, size, HAL_MAX_DELAY);

    //NSS = 1;
    NSS_1;
}

void WriteReg( uint16_t address, uint8_t value )
{
    WriteRegister( address, &value, 1 );
}

void ReadRegister( uint16_t address, uint8_t *buffer, uint16_t size )
{
	uint8_t	txData[4];

    WaitOnBusy( );

    //NSS = 0;
     NSS_0;

 	txData[0] = (uint8_t)RADIO_READ_REGISTER;
 	txData[1] = (uint8_t)(( address & 0xFF00) >> 8 );
 	txData[2] = (uint8_t)(address & 0x00FF);
 	txData[0] = (uint8_t)0;
 	HAL_SPI_Transmit(sx126x_obj.spi->spi, txData, 4, HAL_MAX_DELAY);

 	HAL_SPI_Receive(sx126x_obj.spi->spi, buffer, size, HAL_MAX_DELAY);

     //NSS = 1;
     NSS_1;
}

uint8_t ReadReg( uint16_t address )
{
    uint8_t data;

    ReadRegister( address, &data, 1 );
    return data;
}

void WriteBuffer( uint8_t offset, uint8_t *buffer, uint8_t size )
{
	uint8_t	txData[2];

    WaitOnBusy( );

    //NSS = 0;
    NSS_0;

    txData[0] = RADIO_WRITE_BUFFER;
    txData[1] = offset;
    HAL_SPI_Transmit(sx126x_obj.spi->spi, txData, 2, HAL_MAX_DELAY);

    HAL_SPI_Transmit(sx126x_obj.spi->spi, buffer, size, HAL_MAX_DELAY);

    //NSS = 1;
    NSS_1;
}

void ReadBuffer( uint8_t offset, uint8_t *buffer, uint8_t size )
{
	uint8_t	txData[3];

    WaitOnBusy( );

    //NSS = 0;
    NSS_0;

    txData[0] = RADIO_READ_BUFFER;
    txData[1] = offset;
    txData[2] = 0;
    HAL_SPI_Transmit(sx126x_obj.spi->spi, txData, 3, HAL_MAX_DELAY);

    HAL_SPI_Receive(sx126x_obj.spi->spi, buffer, size, HAL_MAX_DELAY);

    //NSS = 1;
    NSS_1;
}

uint8_t GetDioStatus( void )
{
	// TODO:
	return 0;
//    return ( *DIO3 << 3 ) | ( *DIO2 << 2 ) | ( *DIO1 << 1 ) | ( BUSY << 0 );
}

uint8_t GetDeviceType( void )
{

	return( SX1262 );
}

mp_obj_t sx126x_hal_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
	// Parse arguments
    enum { ARG_rst, ARG_nss, ARG_busy };
    static const mp_arg_t allowed_args[] = {
        { MP_QSTR_rst, 	MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_nss, 	MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
		{ MP_QSTR_busy, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
    };
    // args[0] spi object
	sx126x_obj.spi = spi_from_mp_obj(args[0]);
    
    mp_arg_val_t args_val[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args_val);

	sx126x_obj.rst = pin_find(args_val[ARG_rst].u_obj);
	sx126x_obj.nss = pin_find(args_val[ARG_nss].u_obj);
	sx126x_obj.busy = pin_find(args_val[ARG_busy].u_obj);

	mp_hal_pin_output(sx126x_obj.rst);
    mp_hal_pin_high(sx126x_obj.rst);
	mp_hal_pin_output(sx126x_obj.nss);
    mp_hal_pin_high(sx126x_obj.nss);
	mp_hal_pin_input(sx126x_obj.busy);


	return mp_const_none;
}